#pragma once

#include "my_http_base.h"
#include "my_http_utils.h"
#include "my_http_headers.h"
#include "event/my_event_loop.h"

typedef struct my_http_request_s            my_http_request_t;
typedef struct my_http_response_s           my_http_response_t;

typedef void (*my_http_request_handler_t)(my_http_request_t *request, my_result_t res);

struct my_http_response_s {
    my_http_request_t                       *request;

    // context
    void                                    *data;
    my_http_request_handler_t               handler;

    // the http request line & headers is saved in multiple bufs.
    // these buffers cannot be released during the request process.
    // we can release it at the end of the request.
    my_buf_chain_t                          *header_chain;
    int64_t                                 header_size;

    my_buf_chain_t                          *body_chain;
    my_buf_chain_t                          *output_chain;
    // we can release it at the update or end of the request.
    my_buf_t                                *body_buf;

    // point to the buffer currently being written,
    // it is only a reference.
    my_buf_t                                *last_buf;

    // for request parse.
    my_http_readline_sm_t                   readline_sm;

    int                                     status_code;
    my_str_t                                *reason;

    my_http_headers_t                       headers;
#if 0
    my_http_headers_t                       trailer_headers;
#endif

    int                                     invalid_headers;

    int64_t                                 content_length;
    int64_t                                 content_remain;

    my_http_chunk_sm_t                      *chunk_sm;

    // record errors that occur on the http response.
    my_result_t                             error;

    unsigned                                nobody:1;
    unsigned                                chunked:1;
    unsigned                                seal_header:1;
    unsigned                                start_send_header:1;
    unsigned                                send_header_done:1;
    unsigned                                start_send_body:1;
    unsigned                                last_body_flag:1;
    unsigned                                write_last_chunk_buf:1;
    unsigned                                parse_rl_done:1;
    unsigned                                parse_rh_done:1;
    unsigned                                start_read_header;
    unsigned                                start_read_body:1;
    unsigned                                read_body_done:1;

    // read or send response data done
    unsigned                                done:1;
};

struct my_http_request_s {
    my_conn_t                               *conn;
    my_pool_t                               *pool;
    my_http_response_t                      *response;

    // context
    void                                    *data;
    my_http_request_handler_t               handler;
    my_http_request_handler_t               finalized_handler;

    // the http request line & headers is saved in multiple bufs.
    // these buffers cannot be released during the request process.
    // we can release it at the end of the request.
    my_buf_chain_t                          *header_chain;
    int64_t                                 header_size;

    my_buf_chain_t                          *body_chain;
    my_buf_chain_t                          *output_chain;
    // we can release it at the update or end of the request.
    my_buf_t                                *body_buf;

    // point to the buffer currently being written,
    // it is only a reference.
    my_buf_t                                *last_buf;

    // for request parse.
    my_http_readline_sm_t                   readline_sm;

    my_str_t                                method;
    my_str_t                                raw_uri;
    my_str_t                                raw_args;
    my_str_t                                raw_fragment;

    my_str_t                                uri;
    my_str_t                                uri_ext;
    my_str_t                                args;
    my_str_t                                fragment;

    // for connect request.
    my_str_t                                connect_scheme;
    my_str_t                                connect_host;
    my_str_t                                connect_port;

    int                                     http_version;
    int                                     invalid_headers;

    my_http_headers_t                       headers;
    int64_t                                 content_length;
    int64_t                                 content_remain;

    my_http_chunk_sm_t                      *chunk_sm;

    // record errors that occur on the http request.
    my_result_t                             error;

    // http requests can finalize done only if the counter is 0.
    int64_t                                 counter;

    // for lingering close.
    int64_t                                 lingering_close_body;

    // current request is http client request or http server request.
    unsigned                                client_request:1;
    // parse request line done.
    unsigned                                parse_rl_done:1;
    // parse request header done.
    unsigned                                parse_rh_done:1;
    unsigned                                want_close:1;
    unsigned                                nobody:1;
    unsigned                                chunked:1;
    unsigned                                start_read_header:1;
    unsigned                                start_read_body:1;
    unsigned                                read_body_done:1;
    unsigned                                seal_header:1;
    unsigned                                start_send_header:1;
    unsigned                                send_header_done:1;
    unsigned                                start_send_body:1;
    unsigned                                last_body_flag:1;
    unsigned                                write_last_chunk_buf:1;

    // read or send request data done.
    unsigned                                done:1;

    unsigned                                terminated:1;
    unsigned                                finalized:1;
};

// save header name and value to header params.
my_result_t my_http_parse_header_line(my_str_t *line, my_http_header_t *header);

void my_http_update_uri_ext(my_http_request_t *request);

my_result_t my_http_parse_request_line(my_http_request_t *request);


/////////////////////////////////////////////////////////////////////////////
//                           PUBLIC INTERFACE                             ///
/////////////////////////////////////////////////////////////////////////////

///////////////////////////// http server interface /////////////////////////

void my_http_start_read_request_header(my_http_request_t *request,
    my_http_request_handler_t done_handler, void *data);

void my_http_start_read_request_body(my_http_request_t *request,
    my_http_request_handler_t notify_handler, void *data);

// in the processing of notify_handler, this function needs to be actively
// called in order to continue to read data.
void my_http_enabled_read_request_body(my_http_request_t *request);

// in the processing of notify_handler, if you do not continue to read data,
// you need to display the call to this function.
void my_http_disabled_read_request_body(my_http_request_t *request);

void my_http_start_send_response_header(my_http_request_t *request,
    my_http_request_handler_t done_handler, void *data);

// this function can be called after the body is writeen response->body_chain.
// in the chunked transmission scenario, this method needs to be used to inform that
// the response body has ended.
// this function is usually called before start_response_body or enabled_send_response_body.
// if this function is called in notify_handler, enabled_send_response_body must be called after this function.
// because in the chunked transmission, the last chunk data needs to be sent.
// in content-length or unbounded transport scenarios, this function will be ignored.
void my_http_set_last_response_body_flag(my_http_request_t *request);

// in the processing of notify_handler, this function needs to be actively
// called in order to continue to send data.
void my_http_start_send_response_body(my_http_request_t *request,
    my_http_request_handler_t notify_handler, void *data);

void my_http_enabled_send_response_body(my_http_request_t *request);

/////////////////////////// http client interface //////////////////////////

void my_http_start_send_request_header(my_http_request_t *request,
    my_http_request_handler_t done_handler, void *data);

// this function can be called after the body is writeen request->body_chain.
// in the chunked transmission scenario, this method needs to be used to inform that
// the request body has ended.
// this function is usually called before start_request_body or enabled_send_request_body.
// if this function is called in notify_handler, enabled_send_request_body must be called after this function.
// because in the chunked transmission, the last chunk data needs to be sent.
// in content-length or unbounded transport scenarios, this function will be ignored.
void my_http_set_last_request_body_flag(my_http_request_t *request);

void my_http_start_send_request_body(my_http_request_t *request,
    my_http_request_handler_t notify_handler, void *data);

void my_http_enabled_send_request_body(my_http_request_t *request);

void my_http_start_read_response_header(my_http_request_t *request,
    my_http_request_handler_t done_handler, void *data);

void my_http_start_read_response_body(my_http_request_t *request,
    my_http_request_handler_t notify_handler, void *data);

void my_http_enabled_read_response_body(my_http_request_t *request);

void my_http_disabled_read_response_body(my_http_request_t *request);

///////////////////////////// common interface /////////////////////////////

// abort http request and cancel events callback
void my_http_abort_http_request(my_http_request_t *request);

void my_http_finalize_request(my_http_request_t *request,
    my_http_request_handler_t done_handler, void *data);

my_http_request_t *my_http_request_create(my_conn_t *conn, my_bool_t client_request);

void my_http_request_destroy(my_http_request_t *request);

///////////////////////////////// handlers ////////////////////////////////
static inline void
my_http_server_conn_free_handler(my_conn_t *conn)
{
    my_buf_t        *pre_buf;
    pre_buf = conn->data;

    // is http server pipeline request.
    if (pre_buf) {
        my_buf_free(pre_buf);
    }

    my_conn_close(conn);
    my_thread_free(conn);
}

static inline void
my_http_client_conn_free_handler(my_conn_t *conn)
{
    if (conn->data) {
        MY_LOG_FATAL("unreachable");
    }

    my_conn_close(conn);
    my_thread_free(conn);
}


////////////////////////////////// for test //////////////////////////////////
void my_http_request_dump(my_http_request_t *request);

void my_http_request_body_dump(my_http_request_t *request);